/*
 * Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd. 
 * <https://www.chatopera.com>, Licensed under the Chunsong Public 
 * License, Version 1.0  (the "License"), https://docs.cskefu.com/licenses/v1.html
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * Copyright (C) 2018- Jun. 2023 Chatopera Inc, <https://www.chatopera.com>,  Licensed under the Apache License, Version 2.0, 
 * http://www.apache.org/licenses/LICENSE-2.0
 * Copyright (C) 2017 优客服-多渠道客服系统,  Licensed under the Apache License, Version 2.0, 
 * http://www.apache.org/licenses/LICENSE-2.0
 */
package com.cskefu.cc.persistence.repository;

import com.cskefu.cc.basic.Constants;
import com.cskefu.cc.basic.MainUtils;
import com.cskefu.cc.model.ColumnProperties;
import com.cskefu.cc.util.bi.CubeReportData;
import com.cskefu.cc.util.bi.model.FirstTitle;
import com.cskefu.cc.util.bi.model.Level;
import com.cskefu.cc.util.bi.model.ValueData;

import mondrian.olap.*;
import mondrian.rolap.RolapCubeLevel;
import mondrian.rolap.RolapLevel;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;

public class CubeService {
    private final DataSourceService dataSource;
	
    private final String SCHEMA_DATA_PATH = "WEB-INF/data/mdx/";
    private File schemaFile = null;

    public CubeService(String xml, String path, DataSourceService dataSource, Map<String, Object> requestValues) throws IOException {
        this.dataSource = dataSource;
        File mdxFileDir = new File(path, "mdx");
        if (!mdxFileDir.exists()) {
            mdxFileDir.mkdirs();
        }
        schemaFile = new File(mdxFileDir, MainUtils.getUUID() + ".xml");
        StringWriter writer = new StringWriter();
        IOUtils.copy(CubeService.class.getClassLoader().getResourceAsStream(SCHEMA_DATA_PATH + xml), writer, "UTF-8");
        FileUtils.write(schemaFile, MainUtils.getTemplet(writer.toString(), requestValues), "UTF-8");    //使用系统默认编码
    }

    public CubeService(String xml, String path, DataSourceService dataSource, Map<String, Object> requestValues, boolean isContentStr) throws IOException {
        this.dataSource = dataSource;
        File mdxFileDir = new File(path, "mdx");
        if (!mdxFileDir.exists()) {
            mdxFileDir.mkdirs();
        }
        schemaFile = new File(mdxFileDir, MainUtils.getUUID() + ".xml");
        if (isContentStr) {
            FileUtils.write(schemaFile, MainUtils.getTemplet(xml, requestValues), "UTF-8");    //使用系统默认编码
        } else {
            StringWriter writer = new StringWriter();
            IOUtils.copy(CubeService.class.getClassLoader().getResourceAsStream(SCHEMA_DATA_PATH + xml), writer, "UTF-8");
            FileUtils.write(schemaFile, MainUtils.getTemplet(writer.toString(), requestValues), "UTF-8");    //使用系统默认编码
        }
    }

    public CubeReportData execute(String mdx) throws Exception {
        return execute(mdx, null);
    }

    @SuppressWarnings("deprecation")
    public CubeReportData execute(String mdx, List<ColumnProperties> cols) throws Exception {
        Connection connection = null;
        CubeReportData cubeReportData = new CubeReportData();
        try {
            connection = dataSource.service(schemaFile.getAbsolutePath());
            Query query = connection.parseQuery(mdx);
            Result result = connection.execute(query);
            Axis[] axises = result.getAxes();
            cubeReportData.setData(new ArrayList<>());
            for (int i = 0; i < axises.length; i++) {
                if (i == 0) {
                    cubeReportData.setCol(createTitle(axises[i], i, cols));
                } else {
                    cubeReportData.setRow(createTitle(axises[i], i, cols));
//					cubeReportData.setTotal(axises[i].getDataSize());
                }
            }
            if (cubeReportData.getRow() == null) {
                cubeReportData.setRow(new Level("root", "row", null, 0));
                cubeReportData.getRow().setTitle(new ArrayList<>());
                if (cubeReportData.getRow().getTitle().size() == 0) {
                    List<Level> rowList = new ArrayList<>();
                    rowList.add(new Level("合计", "row", null, 0));
                    cubeReportData.getRow().getTitle().add(rowList);
                }
            }
            getRowData(result.getAxes(), result.getAxes().length - 1, new int[result.getAxes().length], result, cubeReportData.getData(), 0, null, cubeReportData, cols);
            processSum(cubeReportData.getRow(), cubeReportData.getData(), cubeReportData.getRow());
            processSum(cubeReportData.getCol(), cubeReportData.getData(), cubeReportData.getCol());
            cubeReportData.getRow().setTitle(new ArrayList<>());
            processTitle(cubeReportData.getRow(), cubeReportData.getRow());

        } catch (Exception ex) {
            throw ex;
        } finally {
            if (connection != null) {
                connection.close();
            }
            if (schemaFile.exists()) {
                schemaFile.delete();
            }
        }
        return cubeReportData;
    }

    @SuppressWarnings("rawtypes")
    public Level createTitle(Axis axis, int index, List<ColumnProperties> cols) {
        Level level = new Level("root", index == 0 ? "col" : "row", null, 0);
        Map<String, Map> valueMap = new HashMap<>();
        List<Position> posList = axis.getPositions();
        List<String> valueStr = new ArrayList<>();
        List<FirstTitle> firstTitle = new ArrayList<>();
        for (Position pos : posList) {
            StringBuffer strb = new StringBuffer();
            for (int i = 0; i < pos.size(); i++) {
                Member member = pos.get(i);
                RolapLevel cubeLevel = (RolapLevel) member.getLevel();
                int n = 0;
                if (member.getLevel() instanceof RolapCubeLevel && !cubeLevel.getName().contains("All")) {
                    if (level.getFirstTitle() == null) {
                        level.setFirstTitle(firstTitle);
                        FirstTitle first = new FirstTitle();
                        first.setName(cubeLevel.getName());
                        first.setLevel(cubeLevel.getUniqueName());
                        addFirstTitle(level.getFirstTitle(), -1, first);
                        while ((cubeLevel = (RolapLevel) cubeLevel.getParentLevel()) != null && !cubeLevel.getName().contains("All")) {
                            n++;
                            FirstTitle first2 = new FirstTitle();
                            first2.setName(cubeLevel.getName());
                            first2.setLevel(cubeLevel.getUniqueName());
                            if (level.getFirstTitle().size() > firstTitle.size() - i) {
                                addFirstTitle(level.getFirstTitle(), firstTitle.size() - n, first2);
                            } else {
                                addFirstTitle(level.getFirstTitle(), 0, first2);
                            }
                        }
                    } else {
                        boolean isHave = false;
                        for (FirstTitle fr : level.getFirstTitle()) {
                            if (fr.getLevel().equals(cubeLevel.getUniqueName())) {
                                isHave = true;
                                break;
                            }
                        }
                        if (!isHave) {
                            FirstTitle first = new FirstTitle();
                            first.setName(cubeLevel.getName());
                            first.setLevel(cubeLevel.getUniqueName());
                            addFirstTitle(level.getFirstTitle(), -1, first);
                            while ((cubeLevel = (RolapLevel) cubeLevel.getParentLevel()) != null && !cubeLevel.getName().contains("All")) {
                                n++;
                                FirstTitle first2 = new FirstTitle();
                                first2.setName(cubeLevel.getName());
                                first2.setLevel(cubeLevel.getUniqueName());
                                if (level.getFirstTitle().size() > firstTitle.size() - i) {
                                    addFirstTitle(level.getFirstTitle(), firstTitle.size() - n, first2);
                                } else {
                                    addFirstTitle(level.getFirstTitle(), 0, first2);
                                }

                            }
                        }
                    }
                } else if (member.getLevel() instanceof RolapLevel && cubeLevel.getName().equals("MeasuresLevel")) {    //指标列
                    if (level.getFirstTitle() == null) {
                        level.setFirstTitle(firstTitle);
                        FirstTitle first = new FirstTitle();
                        first.setName(Constants.CUBE_TITLE_MEASURE);
                        first.setLevel(cubeLevel.getUniqueName());
                        addFirstTitle(level.getFirstTitle(), -1, first);
                        while ((cubeLevel = (RolapLevel) cubeLevel.getParentLevel()) != null && !cubeLevel.getName().contains("All")) {
                            n++;
                            FirstTitle first2 = new FirstTitle();
                            first2.setName(cubeLevel.getName());
                            first2.setLevel(cubeLevel.getUniqueName());
                            if (level.getFirstTitle().size() > firstTitle.size() - i) {
                                addFirstTitle(level.getFirstTitle(), firstTitle.size() - n, first2);
                            } else {
                                addFirstTitle(level.getFirstTitle(), 0, first2);
                            }
                        }
                    } else {
                        boolean isHave = false;
                        for (FirstTitle fr : level.getFirstTitle()) {
                            if (fr.getLevel().equals(cubeLevel.getUniqueName())) {
                                isHave = true;
                                break;
                            }
                        }
                        if (!isHave) {
                            FirstTitle first = new FirstTitle();
                            first.setName(Constants.CUBE_TITLE_MEASURE);
                            first.setLevel(cubeLevel.getUniqueName());
                            addFirstTitle(level.getFirstTitle(), -1, first);
                            while ((cubeLevel = (RolapLevel) cubeLevel.getParentLevel()) != null && !cubeLevel.getName().contains("All")) {
                                n++;
                                FirstTitle first2 = new FirstTitle();
                                first2.setName(cubeLevel.getName());
                                first2.setLevel(cubeLevel.getUniqueName());
                                if (level.getFirstTitle().size() > firstTitle.size() - i) {
                                    addFirstTitle(level.getFirstTitle(), firstTitle.size() - n, first2);
                                } else {
                                    addFirstTitle(level.getFirstTitle(), 0, first2);
                                }

                            }
                        }
                    }
                }


                if (strb.length() > 0) {
                    strb.append("l__HHHH-A-HHHH__l");
                }
                strb.append(member.getUniqueName().substring(member.getUniqueName().indexOf(".") + 1).replaceAll("\\.\\[\\]", "______R3_SPACE").replaceAll("\\]\\.\\[", "l__HHHH-A-HHHH__l").replaceAll("[\\]\\[]", ""));
            }
            if (cols != null) {
                for (ColumnProperties col : cols) {
                    if (strb.toString().equals(col.getDataname())) {
                        strb = new StringBuffer();
                        strb.append(col.getTitle());
                    }
                }
            }
            valueStr.add(strb.toString().replace("#null", " "));//替换掉所有的#null为空字符串
        }
        int depth = 0;
        for (int inx = 0; inx < valueStr.size(); inx++) {
            String value = valueStr.get(inx);
            Level currentlevel = level;
            String[] levels = value.replaceAll("Measures______", "").split("l__HHHH-A-HHHH__l");
            if (levels.length > depth) {
                depth = levels.length - 1;
            }
            for (int i = 0; i < levels.length; i++) {
                boolean found = false;
                if (currentlevel.getChilderen() == null) {
                    currentlevel.setChilderen(new ArrayList<>());
                }
                if (!levels[i].equals("R3_SPACE")) {
                    for (Level lv : currentlevel.getChilderen()) {
                        if (levels[i].equals(lv.getName())) {
                            currentlevel = lv;
                            found = true;
                            break;
                        }
                    }
                    if (!found) {
                        currentlevel.getChilderen().add(currentlevel = new Level(levels[i], level.getLeveltype(), currentlevel, levels.length - i, inx));
                        if (i == levels.length - 1) {
                            if (currentlevel.getChilderen() == null) {
                                currentlevel.setChilderen(new ArrayList<>());
                            }
                            currentlevel.setIndex(inx);
                            currentlevel.getChilderen().add(new Level("R3_TOTAL", level.getLeveltype(), currentlevel, levels.length - i, inx));
                        }
                        if (level.getFirstTitle() != null && level.getFirstTitle().size() > i) {
                            currentlevel.setDimname(level.getFirstTitle().get(i).getName());
                        }
                    } else {
                        if (i == levels.length - 1) {
                            if (currentlevel.getChilderen() == null) {
                                currentlevel.setChilderen(new ArrayList<>());
                            }
                            currentlevel.setIndex(inx);
                            currentlevel.getChilderen().add(new Level("R3_TOTAL", level.getLeveltype(), currentlevel, levels.length - i, inx));
                        }
                    }
                } else {
                    currentlevel.getChilderen().add(currentlevel = new Level("", level.getLeveltype(), currentlevel, levels.length - i, inx));
                    if (i == levels.length - 1) {
                        if (currentlevel.getChilderen() == null) {
                            currentlevel.setChilderen(new ArrayList<>());
                        }
                        currentlevel.setIndex(inx);
                        currentlevel.getChilderen().add(new Level("R3_TOTAL", level.getLeveltype(), currentlevel, levels.length - i, inx));
                    }
                    if (level.getFirstTitle() != null && level.getFirstTitle().size() > i) {
                        currentlevel.setDimname(level.getFirstTitle().get(i).getName());
                    }
                }
            }
        }

        iterator(valueMap, level, level.getLeveltype());
        level.setDepth(depth);
        for (Level temp : level.getChilderen()) {
            temp.setParent(level);
        }
        sumRowspanColspan(level);
        level.init();    //格式化
        return level;
    }

    /**
     * @param firstTitleList
     * @param title
     */
    private void addFirstTitle(List<FirstTitle> firstTitleList, int index, FirstTitle title) {
        boolean found = false;
        for (FirstTitle firstTitle : firstTitleList) {
            if (firstTitle.getLevel().equals(title.getLevel())) {
                found = true;
                break;
            }
        }
        if (!found) {
            if (index < 0) {
                firstTitleList.add(title);
            } else {
                firstTitleList.add(index, title);
            }
        }
    }

    /**
     * @param axes
     * @param axis
     * @param pos
     * @param result
     * @param dataList
     * @param rowno
     */
    private void getRowData(Axis[] axes, int axis, int[] pos, Result result, List<List<ValueData>> dataList, int rowno, Position position, CubeReportData cubeData, List<ColumnProperties> cols) {
        if (axis < 0) {
            if (dataList.size() <= rowno || dataList.get(rowno) == null) {
                dataList.add(new ArrayList<>());
            }
            Cell cell = result.getCell(pos);
            ValueData valueData = new ValueData(cell.getValue(), cell.getFormattedValue(), null, cell.canDrillThrough(), cell.getDrillThroughSQL(true), position != null && position.size() > 0 ? position.get(position.size() - 1).getName() : "", cell.getCachedFormatString(), cols);
            int rows = 0;
            valueData.setRow(getParentLevel(cubeData.getRow(), rowno, rows));
            dataList.get(rowno).add(valueData);
        } else {
            Axis _axis = axes[axis];
            List<Position> positions = _axis.getPositions();
            for (int i = 0; i < positions.size(); i++) {
                Position posit = positions.get(i);
                pos[axis] = i;
                if (axis == 0) {
                    int row = axis + 1 < pos.length ? pos[axis + 1] : 0;
                    rowno = row;
                }
                getRowData(axes, axis - 1, pos, result, dataList, rowno, posit, cubeData, cols);
            }
        }
    }

    private Level getParentLevel(Level level, int rowno, int rows) {
        if (level != null && level.getChilderen() != null) {
            for (Level lv : level.getChilderen()) {
                rows = rows + lv.getRowspan();
                if (rows == rowno) {
                    while (level.getChilderen() != null && level.getChilderen().size() > 0) {
                        level = level.getChilderen().get(level.getChilderen().size() - 1);
                    }
                }
                if (rows > rowno) {
                    rows = rows - lv.getRowspan();
                    return getParentLevel(lv, rowno, rows);
                }
            }
        }
        return level;
    }

    /**
     * 处理 合计字段
     *
     * @param level
     */
    private void processSum(Level level, List<List<ValueData>> valueDataList, Level root) {
        for (int i = 0; level.getChilderen() != null && i < level.getChilderen().size(); i++) {
            Level child = level.getChilderen().get(i);
//			child.setIndex(i) ;
            if (child.getName().equals("R3_TOTAL") && !child.isTotal() && level.getIndex() < valueDataList.size()) {
                child.setName("合计");
                child.setTotal(true);
                child.setFirst(i == 0);
                if (level.getChilderen().size() > 1 && level.getParent() != null) {
                    child.setValueData(valueDataList.get(level.getIndex()));
                    if (level.getParent().getChilderen().size() > 0) {
//						for(int inx = 0 ; inx < level.getParent().getChilderen().size() ; inx++){
//							if(level.getParent().getChilderen().get(inx).getName().equals(child.getParent().getName())){
////								child.setParent(child.getParent().getParent()) ;
////								level.getParent().getChilderen().add(inx+1 , child);
//								break ;
//							}
//						}
                        child.setDepth(getDepth(child));
                        child.setColspan(root.getFirstTitle().size() - child.getDepth());
//						level.getChilderen().remove(i--) ;
                    }

                } else {
                    if (valueDataList.size() > child.getIndex()) {
                        level.setValueData(valueDataList.get(level.getIndex()));
                    }
                    level.setChilderen(null);
                }
            } else {
                processSum(child, valueDataList, root);
            }
        }
    }

    private int getDepth(Level level) {
        int depth = 0;
        while ((level = level.getParent()) != null) {
            depth++;
        }
        return depth - 1;
    }

    /*
     *
     */
    private void processTitle(Level level, Level root) {
        for (int i = 0; level.getChilderen() != null && i < level.getChilderen().size(); i++) {
            Level child = level.getChilderen().get(i);
            int depth = getDepth(child);
            if (depth >= 0) {
                if (root.getTitle().size() <= depth) {
                    root.getTitle().add(new ArrayList<>());
                }
                Level tempLevel = new Level(child.getName(), child.getNameValue(), child.getLeveltype(), child.getRowspan(), child.getColspan(), child.getValueData(), child.isTotal(), child.isFirst());
                tempLevel.setParent(child);
                root.getTitle().get(depth).add(tempLevel);
            }
            if ((child.getNameValue().equals("R3_TOTAL") && root.getFirstTitle() != null && (depth + 1) < root.getFirstTitle().size()) || (child.getChilderen() == null && root.getFirstTitle() != null && (depth + 1) < root.getFirstTitle().size())) {
                child.setChilderen(new ArrayList<>());
                child.setColspan(root.getFirstTitle().size() - depth);
                if (root.getTitle() != null && root.getTitle().size() > depth) {
                    for (Level title : root.getTitle().get(depth)) {
                        if (title.getName().equals(child.getName())) {
                            title.setColspan(child.getColspan());
                        }
                    }
                }
                Level tempLevel = new Level("TOTAL_TEMP", "R3_TOTAL", child.getLeveltype(), child.getRowspan(), child.getColspan(), child.getValueData(), child.isTotal(), child.isFirst(), child.getDepth() + 1);
                tempLevel.setParent(child);
                child.getChilderen().add(tempLevel);
                child.setValue(null);
            }
            processTitle(child, root);
        }
    }

    /**
     * 处理行列合并单元格
     *
     * @param level
     */
    private void sumRowspanColspan(Level level) {
        if (level.getChilderen() != null) {
            for (int i = 0; level.getChilderen() != null && i < level.getChilderen().size(); i++) {
                Level lv = level.getChilderen().get(i);
                if (lv.getColspan() == 0 && lv.getRowspan() == 0) {
                    sumRowspanColspan(lv);
                }
                if (level.getLeveltype().equals("col")) {
                    level.setColspan(level.getColspan() + lv.getColspan());
                } else {
                    level.setRowspan(level.getRowspan() + lv.getRowspan());
                }
            }
        } else {
            if (level.getLeveltype().equals("col")) {
                level.setColspan(1);
            } else {
                level.setRowspan(1);
            }
        }
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    public static void iterator(Map<String, Map> value, Level level, String leveltype) {
        Iterator<String> iterator = value.keySet().iterator();
        if (level.getChilderen() == null) {
            level.setChilderen(new ArrayList<>());
        }
        while (iterator.hasNext()) {
            String name = iterator.next();
            Level sublevel = new Level(name, leveltype, level, level.getDepth() - 1);
            level.getChilderen().add(sublevel);
            if (value.get(name) != null) {
                iterator(value.get(name), sublevel, leveltype);
            }
        }
    }
}
